#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "matriz_svdlibc.h"
#include "utilidades.h"

#define MATNAME "M"

double calcular_peso(int valor,int df,unsigned long int tf2,unsigned long columnas){
	
	double peso_log = log( (double)(valor + 1) );
	
	double peso_idf = log( (double)(columnas/df) );
	
	double peso_normalizado = ( 0.5 + 0.5/tf2 );
	
	double peso_termino = peso_log*peso_idf*peso_normalizado;
	
	return peso_termino;
	
}

/*
 * Construye una matriz en el formato de la libreria SVDLIBC a partir del
 * archivo abierto "archivo_mergeado". Escribe la matriz en el archivo
 * abierto "matriz_destino". Recibe, ademas, la cantidad total de
 * filas, columnas y elementos distintos de cero.
 */
void construir_matriz_svdlibc(FILE* archivo_mergeado,FILE* matriz_destino,unsigned long filas,unsigned long columnas,unsigned long elementos){
	
	if( !archivo_mergeado || !matriz_destino ) return;
	
	char* entrada_termino = NULL;
	char* entrada_n_docs = NULL;
	char* campo_de_la_entrada = NULL;
	char* entrada_tf2 = NULL;
	unsigned long int tf2 = 0;
	int cantidad_distinta_de_cero = 0;
	int indice_en_la_fila = 0;
	int valor = 0;
	double peso = 0;
	
	// Primera fila con informacion de dimensiones y cantidad de ceros.
	fprintf(matriz_destino,"%lu %lu %lu\n",columnas,filas,elementos);
	
	while( !feof(archivo_mergeado) ){
		
		entrada_termino = leer_linea(archivo_mergeado);
		entrada_n_docs = leer_linea(archivo_mergeado);
		entrada_tf2 = leer_linea(archivo_mergeado);
		
		// Para cada columna:
		//		cantidadDeValoresDistintosDeCero.
		cantidad_distinta_de_cero = atoi(entrada_n_docs);
		free(entrada_n_docs);
		fprintf(matriz_destino,"%d\n",cantidad_distinta_de_cero);
		
		campo_de_la_entrada = strtok(entrada_termino,",");
		
		tf2 = atol(entrada_tf2);
		free(entrada_tf2);

		while( (campo_de_la_entrada = strtok(NULL,",")) ){
			
			//	Para cada valor distinto de cero en la fila:
			//		indiceEnLaFila Valor
			indice_en_la_fila = atoi(campo_de_la_entrada);
			// Por esto (^) es que necesitamos trabajar a los documentos como enteros.
			campo_de_la_entrada = strtok(NULL,",");
			valor = atoi(campo_de_la_entrada);
			peso = calcular_peso(valor,cantidad_distinta_de_cero,tf2,filas);
			fprintf(matriz_destino,"%d %.16f\n",indice_en_la_fila,peso);
			
		}
		
		free(entrada_termino);
		
	}
	
}


/* Escribe la cabecera del formato octave en el archivo destino.
 * Formato:
 *  /usr/share/octave/packages/3.2
	# name: nombre
	# type: sparse matrix
	# nnz: elementos distintos de 0
	# rows: filas
	# columns: columnas
 */
void escribir_cabecera(FILE* matriz_destino , char* nombre , unsigned long filas,unsigned long columnas, unsigned long elementos) {

	char header[400];
	char aux[20];
	strcpy(header,"# Created by Octave 3.2.4, Sat Nov 10 16:37:07 2012 ART <tomas@BOCCARDO-08>\n");
	strcat(header,"# name: ans\n");
	strcat(header,"# type: string\n");
	strcat(header,"# elements: 1\n");
	strcat(header,"# length: 30\n");
	strcat(header, "/usr/share/octave/packages/3.2\n");
	strcat(header, "# name: ");
	strcat(header, nombre);
	strcat(header,"\n");
	strcat(header, "# type: sparse matrix\n");
	strcat(header, "# nnz: ");
	sprintf(aux, "%lu\n", elementos);
	strcat(header, aux);
	strcat(header, "# rows: ");
	sprintf(aux, "%lu\n", filas);
	strcat(header, aux);
	strcat(header, "# columns: ");
	sprintf(aux, "%lu\n", columnas);
	strcat(header, aux);
	fprintf(matriz_destino, "%s", header);
}

void construir_matriz_octave(FILE* archivo_mergeado,FILE* matriz_destino,unsigned long filas,unsigned long columnas,unsigned long elementos){

	if( !archivo_mergeado || !matriz_destino ) return;

	//Escribe la cabezera del formato octave en el archivo destino.
	escribir_cabecera(matriz_destino, MATNAME, filas, columnas, elementos);

	char* entrada_termino = NULL;
	char* entrada_n_docs = NULL;
	char* campo_de_la_entrada = NULL;

	char* entrada_tf2 = NULL;
	unsigned long int tf2 = 0;
	long unsigned indice_de_la_columna = 0;
	long unsigned indice_de_la_fila = 1;

	int valor = 0;
	int cantidad_distinta_de_cero = 0;
	double peso = 0;

	while( !feof(archivo_mergeado) ){

		entrada_termino = leer_linea(archivo_mergeado);
		entrada_n_docs = leer_linea(archivo_mergeado);
		entrada_tf2 = leer_linea(archivo_mergeado);

		campo_de_la_entrada = strtok(entrada_termino,",");
		
		cantidad_distinta_de_cero = atoi(entrada_n_docs);
		free(entrada_n_docs);
		
		tf2 = atol(entrada_tf2);
		free(entrada_tf2);

		/*
		 * Posible construccion del indice de terminos aca.
		 */

		while( (campo_de_la_entrada = strtok(NULL,",")) ){

			indice_de_la_fila = atoi(campo_de_la_entrada);
			// Por esto (^) es que necesitamos trabajar a los documentos como enteros.
			campo_de_la_entrada = strtok(NULL,",");
			valor = atoi(campo_de_la_entrada);

			peso = calcular_peso(valor,cantidad_distinta_de_cero,tf2,columnas);
			fprintf(matriz_destino,"%lu %lu %.16f\n",indice_de_la_fila + 1,indice_de_la_columna + 1,peso);


		}

		indice_de_la_columna += 1;

		free(entrada_termino);
	}

}



/*
 * Construye la matriz luego de realizarse el merge. Recibe los nombres de los
 * archivos para su apertura y la misma informacion acerca de las dimensiones
 * y ceros de la matriz que la funcion antes descripta.
 */
void construir_matriz_post_merge(char* merge_file,char* matrix_file,unsigned long filas,unsigned long columnas,unsigned long elementos){
	
	FILE* input = fopen(merge_file,"r");
	FILE* output = fopen(matrix_file,"w");
	
	construir_matriz_svdlibc(input,output,filas,columnas,elementos);
	printf("Matriz final: \"%s\"\n\n",matrix_file);
	
	fclose(input);
	fclose(output);
	
}

